// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <pollux/testing/common/faulty_file.h>

namespace kumo::pollux::tests::utils {

FaultyReadFile::FaultyReadFile(
    const std::string& path,
    std::shared_ptr<ReadFile> delegatedFile,
    FileFaultInjectionHook injectionHook,
    melon::Executor* executor)
    : path_(path),
      delegatedFile_(std::move(delegatedFile)),
      injectionHook_(std::move(injectionHook)),
      executor_(executor) {
  POLLUX_CHECK_NOT_NULL(delegatedFile_);
}

std::string_view FaultyReadFile::pread(
    uint64_t offset,
    uint64_t length,
    void* buf,
    filesystems::File::IoStats* stats) const {
  if (injectionHook_ != nullptr) {
    FaultFileReadOperation op(path_, offset, length, buf);
    injectionHook_(&op);
    if (!op.delegate) {
      return std::string_view(static_cast<char*>(op.buf), op.length);
    }
  }
  return delegatedFile_->pread(offset, length, buf, stats);
}

uint64_t FaultyReadFile::preadv(
    uint64_t offset,
    const std::vector<melon::Range<char*>>& buffers,
    filesystems::File::IoStats* stats) const {
  if (injectionHook_ != nullptr) {
    FaultFileReadvOperation op(path_, offset, buffers);
    injectionHook_(&op);
    if (!op.delegate) {
      return op.readBytes;
    }
  }
  return delegatedFile_->preadv(offset, buffers, stats);
}

melon::SemiFuture<uint64_t> FaultyReadFile::preadvAsync(
    uint64_t offset,
    const std::vector<melon::Range<char*>>& buffers,
    filesystems::File::IoStats* stats) const {
  // TODO: add fault injection for async read later.
  if (delegatedFile_->hasPreadvAsync() || executor_ == nullptr) {
    return delegatedFile_->preadvAsync(offset, buffers, stats);
  }
  auto promise = std::make_unique<melon::Promise<uint64_t>>();
  melon::SemiFuture<uint64_t> future = promise->getSemiFuture();
  executor_->add([this,
                  _promise = std::move(promise),
                  _offset = offset,
                  _buffers = buffers,
                  _stats = stats]() {
    auto delegateFuture =
        delegatedFile_->preadvAsync(_offset, _buffers, _stats);
    _promise->setValue(delegateFuture.wait().value());
  });
  return future;
}

FaultyWriteFile::FaultyWriteFile(
    const std::string& path,
    std::shared_ptr<WriteFile> delegatedFile,
    FileFaultInjectionHook injectionHook)
    : path_(path),
      delegatedFile_(std::move(delegatedFile)),
      injectionHook_(std::move(injectionHook)) {
  POLLUX_CHECK_NOT_NULL(delegatedFile_);
}

void FaultyWriteFile::append(std::string_view data) {
  if (injectionHook_ != nullptr) {
    FaultFileAppendOperation op(path_, data);
    injectionHook_(&op);
    if (!op.delegate) {
      return;
    }
  }
  delegatedFile_->append(data);
}

void FaultyWriteFile::append(std::unique_ptr<melon::IOBuf> data) {
  delegatedFile_->append(std::move(data));
}

void FaultyWriteFile::write(
    const std::vector<iovec>& iovecs,
    int64_t offset,
    int64_t length) {
  if (injectionHook_ != nullptr) {
    FaultFileWriteOperation op(path_, iovecs, offset, length);
    injectionHook_(&op);
    if (!op.delegate) {
      return;
    }
  }
  delegatedFile_->write(iovecs, offset, length);
}

void FaultyWriteFile::truncate(int64_t newSize) {
  delegatedFile_->truncate(newSize);
}

void FaultyWriteFile::flush() {
  delegatedFile_->flush();
}

void FaultyWriteFile::setAttributes(
    const melon::F14FastMap<std::string, std::string>& attributes) {
  delegatedFile_->setAttributes(attributes);
}

melon::F14FastMap<std::string, std::string> FaultyWriteFile::getAttributes()
    const {
  return delegatedFile_->getAttributes();
}

void FaultyWriteFile::close() {
  delegatedFile_->close();
}

uint64_t FaultyWriteFile::size() const {
  return delegatedFile_->size();
}

const std::string FaultyWriteFile::getName() const {
  return delegatedFile_->getName();
}
} // namespace kumo::pollux::tests::utils
